<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="google-site-verification" content="xBT4GhYoi5qRD5tr338pgPM5OWHHIDR6mNg1a3euekI" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="智红的博客">
    <meta name="keyword"  content="智红">
    <link rel="shortcut icon" href="/img/favicon.ico">

    <title>
        
        apue线程 - 智红的博客
        
    </title>

    <!-- Custom CSS -->
    
<link rel="stylesheet" href="/css/aircloud.css">

    
<link rel="stylesheet" href="/css/gitment.css">

    <!--<link rel="stylesheet" href="https://imsun.github.io/gitment/style/default.css">-->
    <link href="//at.alicdn.com/t/font_620856_pl6z7sid89qkt9.css" rel="stylesheet" type="text/css">
    <!-- ga & ba script hoook -->
    <script></script>
<meta name="generator" content="Hexo 5.3.0"></head>

<body>

<div class="site-nav-toggle" id="site-nav-toggle">
    <button>
        <span class="btn-bar"></span>
        <span class="btn-bar"></span>
        <span class="btn-bar"></span>
    </button>
</div>

<div class="index-about">
    <i> 好记性不如烂键盘呐 </i>
</div>

<div class="index-container">
    
    <div class="index-left">
        
<div class="nav" id="nav">
    <div class="avatar-name">
        <div class="avatar ">
            <img src="/img/avatar.jpg" />
        </div>
        <div class="name">
            <i>Zhihong Li</i>
        </div>
    </div>
    <div class="contents" id="nav-content">
        <ul>
            <li >
                <a href="/">
                    <i class="iconfont icon-shouye1"></i>
                    <span>主页</span>
                </a>
            </li>
            <li >
                <a href="/tags">
                    <i class="iconfont icon-biaoqian1"></i>
                    <span>标签</span>
                </a>
            </li>
            <li >
                <a href="/archive">
                    <i class="iconfont icon-guidang2"></i>
                    <span>存档</span>
                </a>
            </li>
            <li >
                <a href="/about/">
                    <i class="iconfont icon-guanyu2"></i>
                    <span>关于</span>
                </a>
            </li>
            
            <li>
                <a id="search">
                    <i class="iconfont icon-sousuo1"></i>
                    <span>搜索</span>
                </a>
            </li>
            
        </ul>
    </div>
    
        <div id="toc" class="toc-article">
    <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#apue%E7%BA%BF%E7%A8%8B"><span class="toc-text">apue线程</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#1-%E7%BA%BF%E7%A8%8B%E7%9A%84%E6%A6%82%E5%BF%B5"><span class="toc-text">1.线程的概念</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9F%A5%E7%9C%8B%E7%BA%BF%E7%A8%8B"><span class="toc-text">查看线程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#pthread-equal"><span class="toc-text">pthread_equal</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#pthread-self"><span class="toc-text">pthread_self</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-%E7%BA%BF%E7%A8%8B%E7%9A%84%E5%88%9B%E5%BB%BA-amp-%E7%BB%88%E6%AD%A2-amp-%E5%8F%96%E6%B6%88-amp-%E6%A0%88%E6%B8%85%E7%90%86"><span class="toc-text">2.线程的创建&amp;终止&amp;取消&amp;栈清理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#pthread-create"><span class="toc-text">pthread_create</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#pthread-exit"><span class="toc-text">pthread_exit</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#pthread-join"><span class="toc-text">pthread_join</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A0%88%E6%B8%85%E7%90%86"><span class="toc-text">栈清理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#pthread-cancel"><span class="toc-text">pthread_cancel</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#pthread-setcanceltype"><span class="toc-text">pthread_setcanceltype</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#pthread-testcancel"><span class="toc-text">pthread_testcancel</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#pthread-detach"><span class="toc-text">pthread_detach</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BE%8B0%EF%BC%9A%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%AD%9B%E8%B4%A8%E6%95%B0"><span class="toc-text">例0：多线程筛质数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BE%8Be%EF%BC%9A%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%AD%9B%E8%B4%A8%E6%95%B0"><span class="toc-text">例e：多线程筛质数</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#3-%E7%BA%BF%E7%A8%8B%E5%90%8C%E6%AD%A5"><span class="toc-text">3.线程同步</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BA%92%E6%96%A5%E9%87%8F-pthead-mutex-t"><span class="toc-text">互斥量(pthead_mutex_t)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#pthread-mutex-xxx"><span class="toc-text">pthread_mutex_xxx</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BE%8B%EF%BC%9A%E5%A4%9A%E7%BA%BF%E7%A8%8B%E8%AF%BB%E5%86%99%E6%96%87%E4%BB%B6"><span class="toc-text">例：多线程读写文件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BE%8B%EF%BC%9A4%E4%B8%AA%E7%BA%BF%E7%A8%8B%E5%88%86%E5%88%AB%E6%89%93%E5%8D%B0abcd%EF%BC%8C%E4%B8%94%E6%8C%89%E9%A1%BA%E5%BA%8F%EF%BC%8C%E6%8C%81%E7%BB%AD5%E7%A7%92"><span class="toc-text">例：4个线程分别打印abcd，且按顺序，持续5秒</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%AE%A9%E5%87%BAcpu"><span class="toc-text">让出cpu</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BE%8B%EF%BC%9AN%E4%B8%AA%E7%BA%BF%E7%A8%8B%E5%8E%BB%E8%AE%A1%E7%AE%97%E6%9F%90%E4%B8%80%E8%8C%83%E5%9B%B4%E5%86%85%E7%9A%84%E8%B4%A8%E6%95%B0"><span class="toc-text">例：N个线程去计算某一范围内的质数</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#pthread-once"><span class="toc-text">pthread_once</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BE%8B%EF%BC%9A%E6%9F%A5%E8%AF%A2%E6%B3%95%E4%BB%A4%E7%89%8C%E6%A1%B6"><span class="toc-text">例：查询法令牌桶</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9D%A1%E4%BB%B6%E5%8F%98%E9%87%8F-pthead-cond-t"><span class="toc-text">条件变量(pthead_cond_t)</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BE%8B%EF%BC%9A%E9%80%9A%E7%9F%A5%E6%B3%95%E5%AE%9E%E7%8E%B0%E4%BB%A4%E7%89%8C%E6%A1%B6"><span class="toc-text">例：通知法实现令牌桶</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#4-%E7%BA%BF%E7%A8%8B%E5%B1%9E%E6%80%A7-amp-%E5%90%8C%E6%AD%A5%E5%B1%9E%E6%80%A7"><span class="toc-text">4.线程属性&amp;同步属性</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#5-%E9%87%8D%E5%85%A5-amp-%E7%BA%BF%E7%A8%8B%E4%B8%8E%E4%BF%A1%E5%8F%B7-amp-%E7%BA%BF%E7%A8%8B%E4%B8%8Efork"><span class="toc-text">5.重入&amp;线程与信号&amp;线程与fork</span></a></li></ol></li></ol>
</div>
    
</div>


<div class="search-field" id="search-field">
    <div class="search-container">
        <div class="search-input">
            <span id="esc-search"> <i class="icon-fanhui iconfont"></i></span>
            <input id="search-input"/>
            <span id="begin-search">搜索</span>
        </div>
        <div class="search-result-container" id="search-result-container">

        </div>
    </div>
</div>

        <div class="index-about-mobile">
            <i> 好记性不如烂键盘呐 </i>
        </div>
    </div>
    
    <div class="index-middle">
        <!-- Main Content -->
        


<div class="post-container">
    <div class="post-title">
        apue线程
    </div>

    <div class="post-meta">
        <span class="attr">发布于：<span>2020-09-01 11:41:53</span></span>
        
        <span class="attr">标签：/
        
        <a class="tag" href="/tags/#apue" title="apue">apue</a>
        <span>/</span>
        
        
        </span>
        <span class="attr">访问：<span id="busuanzi_value_page_pv"></span>
</span>
</span>
    </div>
    <div class="post-content no-indent">
        <h1 id="apue线程"><a href="#apue线程" class="headerlink" title="apue线程"></a>apue线程</h1><h2 id="1-线程的概念"><a href="#1-线程的概念" class="headerlink" title="1.线程的概念"></a>1.线程的概念</h2><p>线程就是一个正在运行的函数。</p>
<p>实际项目中多线程用得比较多，因为多线程是先有标准后有实现的，所以不会向多进程那样在不同平台上有许多不同的情况。</p>
<p>C 语言线程有很多标准，POSIX 是其中的一种（还有比如openmp标准线程）。POSIX 是一套标准，而不是一种实现。所以 POSIX 只是规定了 pthread_t 作为线程标识符，但是并没有规定它必须是由什么类型组成的。在有的平台上它可能是 int，有些平台上它可能是 struct，还有些平台上它可能是 union，所以不要直接操作这个类型，而是要使用 POSIX 规定的各种线程函数来操作它。</p>
<p>有木有觉得像标准 IO 里 FILE 的赶脚？没错，标准制定出来的很多东西都是这种风格的，它为你提供一个数据类型而不让你直接对这个类型操作，要通过它定义的一系列函数来实现对这个类型的操作，这样就在各个平台上实现统一的接口了，所以这样做才能让标准制定出来的东西具有较好的可移植性。</p>
<p>pthread_t 是个很重要的东西，我们所有使用 POSIX 标准的线程操作都是围绕着它来进行的，通过它配合各种函数就可以对线程进行各种花样作死的玩了。:)</p>
<p>线程没有父子关系，是兄弟关系，可以相互收尸（别说主线程，建议说main线程）。线程间比进程间通讯简单，因为线程共用一片地址空间。</p>
<h3 id="查看线程"><a href="#查看线程" class="headerlink" title="查看线程"></a>查看线程</h3><p><code>ps axf</code> 查看进程关系</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># li @ evpower in ~ [14:31:02]</span></span><br><span class="line">$ ps axf</span><br><span class="line">    PID TTY      STAT   TIME COMMAND</span><br><span class="line">      2 ?        S      0:00 [kthreadd]</span><br><span class="line">      3 ?        I&lt;     0:00  \_ [rcu_gp]</span><br><span class="line">      4 ?        I&lt;     0:00  \_ [rcu_par_gp]</span><br><span class="line">      6 ?        I&lt;     0:00  \_ [kworker/0:0H-kblockd]</span><br><span class="line">      9 ?        I&lt;     0:00  \_ [mm_percpu_wq]</span><br><span class="line">...</span><br><span class="line">      1 ?        Ss     0:03 /sbin/init splash</span><br><span class="line">    689 ?        Ss     0:00 avahi-daemon: running [evpower.local]</span><br><span class="line">    738 ?        S      0:00  \_ avahi-daemon: chroot helper</span><br><span class="line">    701 ?        Ss     0:00 /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers</span><br><span class="line">    829 ?        Ssl    0:00 /usr/sbin/gdm3</span><br><span class="line">   1610 ?        Sl     0:00  \_ gdm-session-worker [pam/gdm-password]</span><br><span class="line">   1712 tty2     Ssl+   0:00      \_ /usr/lib/gdm3/gdm-x-session --register-session --run-script i3</span><br><span class="line">   1714 tty2     Sl+    2:09          \_ /usr/lib/xorg/Xorg vt2 -displayfd 3 -auth /run/user/1000/gdm/Xa</span><br><span class="line">   1745 tty2     S+     0:02          \_ i3</span><br><span class="line">   1824 ?        Ss     0:00              \_ /usr/bin/ssh-agent /usr/bin/im-launch i3</span><br></pre></td></tr></table></figure>
<p><code>ps axm</code> 查看进程详细信息，包括线程（<code>- -</code>表示线程）</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">    PID TTY      STAT   TIME COMMAND</span><br><span class="line">      1 ?        -      0:04 /sbin/init splash</span><br><span class="line">      - -        Ss     0:04 -</span><br><span class="line">      2 ?        -      0:00 [kthreadd]</span><br><span class="line">      - -        S      0:00 -</span><br><span class="line">      3 ?        -      0:00 [rcu_gp]</span><br><span class="line">      - -        I&lt;     0:00 -</span><br><span class="line">...</span><br><span class="line">    693 ?        -      0:03 /usr/sbin/NetworkManager --no-daemon</span><br><span class="line">      - -        Ssl    0:03 -</span><br><span class="line">      - -        Ssl    0:00 -</span><br><span class="line">      - -        Ssl    0:00 -</span><br><span class="line">    700 ?        -      0:00 /usr/sbin/irqbalance --foreground</span><br><span class="line">      - -        Ssl    0:00 -</span><br><span class="line">      - -        Ssl    0:00 -</span><br><span class="line">    701 ?        -      0:00 /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers</span><br><span class="line">      - -        Ss     0:00 -</span><br></pre></td></tr></table></figure>
<p><code>ps ax
-L</code>也可以查进程与线程（<code>PID</code>是进程id,<code>LWP</code>是轻量级进程id，常称线程id，也占一个PID）</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">    PID     LWP TTY      STAT   TIME COMMAND</span><br><span class="line">      1       1 ?        Ss     0:04 /sbin/init splash</span><br><span class="line">      2       2 ?        S      0:00 [kthreadd]</span><br><span class="line">      3       3 ?        I&lt;     0:00 [rcu_gp]</span><br><span class="line">...</span><br><span class="line">    708     708 ?        Ssl    0:00 /usr/lib/policykit-1/polkitd --no-debug</span><br><span class="line">    708     715 ?        Ssl    0:00 /usr/lib/policykit-1/polkitd --no-debug</span><br><span class="line">    708     769 ?        Ssl    0:00 /usr/lib/policykit-1/polkitd --no-debug</span><br><span class="line">    710     710 ?        Ssl    0:00 /usr/sbin/rsyslogd -n -iNONE</span><br><span class="line">    710     764 ?        Ssl    0:00 /usr/sbin/rsyslogd -n -iNONE</span><br><span class="line">    710     765 ?        Ssl    0:00 /usr/sbin/rsyslogd -n -iNONE</span><br><span class="line">    710     766 ?        Ssl    0:00 /usr/sbin/rsyslogd -n -iNONE</span><br></pre></td></tr></table></figure>
<p>Linux以线程为维护单位，也就是PID，如果线程没有被收尸，那尸体就占用一个PID。</p>
<h3 id="pthread-equal"><a href="#pthread-equal" class="headerlink" title="pthread_equal"></a>pthread_equal</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//比较两个线程标识符是否相同，相同返回非0，不同则返回0。</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_equal</span><span class="params">(<span class="keyword">pthread_t</span> t1, <span class="keyword">pthread_t</span> t2)</span></span>;</span><br></pre></td></tr></table></figure>
<p>为什么不能使用 <code>if (t1 == t2)</code> 的方式比较两个线程标识符呢？因为各系统实现不一样，你不知道 pthread_t 是什么类型的，所以永远不要自己直接操作它。</p>
<h3 id="pthread-self"><a href="#pthread-self" class="headerlink" title="pthread_self"></a>pthread_self</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//获得当前线程 ID，永远不出错</span></span><br><span class="line"><span class="function"><span class="keyword">pthread_t</span> <span class="title">pthread_self</span><span class="params">(<span class="keyword">void</span>)</span></span>;</span><br></pre></td></tr></table></figure>
<h2 id="2-线程的创建-amp-终止-amp-取消-amp-栈清理"><a href="#2-线程的创建-amp-终止-amp-取消-amp-栈清理" class="headerlink" title="2.线程的创建&amp;终止&amp;取消&amp;栈清理"></a>2.线程的创建&amp;终止&amp;取消&amp;栈清理</h2><h3 id="pthread-create"><a href="#pthread-create" class="headerlink" title="pthread_create"></a>pthread_create</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//创建线程，成功返回0，失败返回errno</span></span><br><span class="line"><span class="comment">//某些平台上 errno 是全局变量，为避免在多线程的情况下出现竞争，POSIX 指定在失败的时直接返回 errno</span></span><br><span class="line"><span class="comment">//创建后的线程id回填到thread</span></span><br><span class="line"><span class="comment">//attr指定创建线程具有的属性(常用NULL就够用)</span></span><br><span class="line"><span class="comment">//start_routine 指定函数也就是线程主体</span></span><br><span class="line"><span class="comment">//arg指定传给start_routine的参数</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_create</span><span class="params">(<span class="keyword">pthread_t</span> *thread, <span class="keyword">const</span> <span class="keyword">pthread_attr_t</span> *attr,</span></span></span><br><span class="line"><span class="function"><span class="params">                    <span class="keyword">void</span> *(*start_routine) (<span class="keyword">void</span> *), <span class="keyword">void</span> *arg)</span></span>;</span><br></pre></td></tr></table></figure>
<h3 id="pthread-exit"><a href="#pthread-exit" class="headerlink" title="pthread_exit"></a>pthread_exit</h3><p>线程终止有3中方式，<code>pthread_exit</code> 只是其一</p>
<ol>
<li>线程从启动例程返回，返回值就是线程退出码</li>
<li>线程可以被同一进程中的其他线程取消</li>
<li>线程调用<code>pthread_exit()</code></li>
</ol>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//终止线程，并通过retval返回一个值</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">pthread_exit</span><span class="params">(<span class="keyword">void</span> *retval)</span></span>;</span><br></pre></td></tr></table></figure>
<h3 id="pthread-join"><a href="#pthread-join" class="headerlink" title="pthread_join"></a>pthread_join</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//收thread的尸体，并把thread返回的值回填到retval</span></span><br><span class="line"><span class="comment">//成功返回0，失败返回errno</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_join</span><span class="params">(<span class="keyword">pthread_t</span> thread, <span class="keyword">void</span> **retval)</span></span>;</span><br></pre></td></tr></table></figure>
<p>类似进程里面的<code>wait()</code></p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Filename: creat1.c</span></span><br><span class="line"><span class="comment"> * No.83-84.线程-线程创建&amp;线程终止和栈清理</span></span><br><span class="line"><span class="comment"> * $ ps axm</span></span><br><span class="line"><span class="comment"> * $ ps ax -L</span></span><br><span class="line"><span class="comment"> * Description:</span></span><br><span class="line"><span class="comment"> * $ gcc -Wall creat1.c -o creat1 -lpthread</span></span><br><span class="line"><span class="comment"> * Last modified: humble 2020-09-01 15:29:48</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="comment">//#include &lt;unistd.h&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">func</span><span class="params">(<span class="keyword">void</span> *p)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;func %d\n&quot;</span>, *(<span class="keyword">int</span> *)p);</span><br><span class="line">    pthread_exit(<span class="number">3</span>);</span><br><span class="line">    <span class="comment">//pthread_exit(NULL);</span></span><br><span class="line">    <span class="comment">//return NULL; //pthread_exit可以实现线程清理，retuan NULL不可以</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> **argv)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i = <span class="number">2</span>;</span><br><span class="line">    <span class="keyword">int</span> retval = <span class="number">-1</span>;</span><br><span class="line">    <span class="keyword">pthread_t</span> tid;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;begin&quot;</span>);</span><br><span class="line">    <span class="keyword">int</span> err = pthread_create(&amp;tid, <span class="literal">NULL</span>, func, (<span class="keyword">void</span> *)&amp;i);</span><br><span class="line">    <span class="keyword">if</span>(err)&#123;</span><br><span class="line">        <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;pthread_create():%s\n&quot;</span>, strerror(err));</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//线程的调度取决于调度器的策略</span></span><br><span class="line">    <span class="comment">//调用pthread_create后，可能main线程先往下运行，也可能func线程先运行</span></span><br><span class="line">    <span class="comment">//所以&quot;func 7&quot;和&quot;main&quot;打印顺序可能不确定</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//sleep(1);</span></span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;main&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//为tid收尸，并用retval接收tid返回值</span></span><br><span class="line">    pthread_join(tid, &amp;retval); <span class="comment">//类似进程的wati()</span></span><br><span class="line"></span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;end %d\n&quot;</span>, retval);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [18:32:28]</span></span><br><span class="line">$ gcc -Wall creat1.c -o creat1 -lpthread</span><br><span class="line">creat1.c: In <span class="keyword">function</span> ‘func’:</span><br><span class="line">creat1.c:18:18: warning: passing argument 1 of ‘pthread_exit’ makes pointer from <span class="built_in">integer</span> without a cast [-Wint-conversion]</span><br><span class="line">   18 |     pthread_exit(3);</span><br><span class="line">      |                  ^</span><br><span class="line">      |                  |</span><br><span class="line">      |                  int</span><br><span class="line">In file included from creat1.c:12:</span><br><span class="line">/usr/include/pthread.h:207:33: note: expected ‘void *’ but argument is of <span class="built_in">type</span> ‘int’</span><br><span class="line">  207 | extern void pthread_exit (void *__retval) __attribute__ ((__noreturn__));</span><br><span class="line">      |                           ~~~~~~^~~~~~~~</span><br><span class="line">creat1.c: In <span class="keyword">function</span> ‘main’:</span><br><span class="line">creat1.c:44:23: warning: passing argument 2 of ‘pthread_join’ from incompatible pointer <span class="built_in">type</span> [-Wincompatible-pointer-types]</span><br><span class="line">   44 |     pthread_join(tid, &amp;retval); //类似进程的wati()</span><br><span class="line">      |                       ^~~~~~~</span><br><span class="line">      |                       |</span><br><span class="line">      |                       int *</span><br><span class="line">In file included from creat1.c:12:</span><br><span class="line">/usr/include/pthread.h:215:49: note: expected ‘void **’ but argument is of <span class="built_in">type</span> ‘int *’</span><br><span class="line">  215 | extern int pthread_join (pthread_t __th, void **__thread_return);</span><br><span class="line">      |                                          ~~~~~~~^~~~~~~~~~~~~~~</span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [18:40:25]</span></span><br><span class="line">$ ./creat1</span><br><span class="line">begin</span><br><span class="line">main</span><br><span class="line">func 2</span><br><span class="line">end 3</span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [18:40:27]</span></span><br><span class="line">$</span><br></pre></td></tr></table></figure>
<h3 id="栈清理"><a href="#栈清理" class="headerlink" title="栈清理"></a>栈清理</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//挂钩子函数 routine</span></span><br><span class="line"><span class="comment">//arg指定传给routine 的参数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">pthread_cleanup_push</span><span class="params">(<span class="keyword">void</span> (*routine)(<span class="keyword">void</span> *), <span class="keyword">void</span> *arg)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//根据 execute 值为真/假判断是否调用钩子函数</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">pthread_cleanup_pop</span><span class="params">(<span class="keyword">int</span> execute)</span></span>;</span><br></pre></td></tr></table></figure>
<p>类似<code>atexit()</code>挂钩子函数。用钩子函数对线程进行清理，钩子函数被调用顺序跟注册时相反。</p>
<p>这两个是带参的宏而不是函数，所以必须成对使用，而且必须先使用 pthread_cleanup_push 再使用  pthread_cleanup_pop，否则会报语法错误，括号不匹配。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Filename: cleanup.c</span></span><br><span class="line"><span class="comment"> * No.84.线程-线程终止和栈清理</span></span><br><span class="line"><span class="comment"> * $ ps axm</span></span><br><span class="line"><span class="comment"> * $ ps ax -L</span></span><br><span class="line"><span class="comment"> * Description:</span></span><br><span class="line"><span class="comment"> * $ gcc -Wall cleanup.c -o cleanup -lpthread</span></span><br><span class="line"><span class="comment"> * Last modified: humble 20200411 17:32</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//pthread_cleanup_push 和 pthread_cleanup_pop 需要成对使用，位置无所谓</span></span><br><span class="line"><span class="comment">//demo 不为0或1，则编译报错</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> demo (0) <span class="comment">//1</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">func_cleanup</span><span class="params">(<span class="keyword">void</span> *p)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">puts</span>(p);</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">func</span><span class="params">(<span class="keyword">void</span> *p)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;pthread func is working&quot;</span>);</span><br><span class="line">    pthread_cleanup_push(func_cleanup, <span class="string">&quot;cleanup1&quot;</span>);</span><br><span class="line">    pthread_cleanup_push(func_cleanup, <span class="string">&quot;cleanup2&quot;</span>);</span><br><span class="line">    pthread_cleanup_push(func_cleanup, <span class="string">&quot;cleanup3&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;push over&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> (demo == 0)</span></span><br><span class="line">    pthread_cleanup_pop(<span class="number">1</span>);</span><br><span class="line">    pthread_cleanup_pop(<span class="number">0</span>);</span><br><span class="line">    pthread_cleanup_pop(<span class="number">0</span>);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">    pthread_exit(<span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> (demo == 1)</span></span><br><span class="line">    <span class="comment">//如果 pthread_cleanup_pop 放在pthread_exit后面，传参execute永远为真</span></span><br><span class="line">    pthread_cleanup_pop(<span class="number">1</span>);</span><br><span class="line">    pthread_cleanup_pop(<span class="number">0</span>);</span><br><span class="line">    pthread_cleanup_pop(<span class="number">0</span>);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> **argv)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">pthread_t</span> tid;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;begin&quot;</span>);</span><br><span class="line">    <span class="keyword">int</span> err = pthread_create(&amp;tid, <span class="literal">NULL</span>, func, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="keyword">if</span>(err)&#123;</span><br><span class="line">        <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;pthread_create():%s\n&quot;</span>, strerror(err));</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    pthread_join(tid, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="built_in">puts</span>(<span class="string">&quot;end&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [19:07:58]</span></span><br><span class="line">$ gcc -Wall cleanup.c -o cleanup -lpthread <span class="comment">#demo0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [19:08:00]</span></span><br><span class="line">$ ./cleanup</span><br><span class="line">begin</span><br><span class="line">pthread func is working</span><br><span class="line">push over</span><br><span class="line">cleanup3</span><br><span class="line">end</span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [19:08:04]</span></span><br><span class="line">$ vi cleanup.c</span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [19:08:13]</span></span><br><span class="line">$ gcc -Wall cleanup.c -o cleanup -lpthread <span class="comment">#demo1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [19:08:24]</span></span><br><span class="line">$ ./cleanup</span><br><span class="line">begin</span><br><span class="line">pthread func is working</span><br><span class="line">push over</span><br><span class="line">cleanup3</span><br><span class="line">cleanup2</span><br><span class="line">cleanup1</span><br><span class="line">end</span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [19:08:26]</span></span><br><span class="line">$</span><br></pre></td></tr></table></figure>
<h3 id="pthread-cancel"><a href="#pthread-cancel" class="headerlink" title="pthread_cancel"></a>pthread_cancel</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//取消同一进程中的线程thread</span></span><br><span class="line"><span class="comment">//成功返回0，失败返回errno</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_cancel</span><span class="params">(<span class="keyword">pthread_t</span> thread)</span></span>;</span><br></pre></td></tr></table></figure>
<p>当一个线程没有必要继续执行下去时，又没法直接为它收尸，就需要先<code>pthread_cancel</code>取消这个线程，再<code>pthread_join</code>收尸。</p>
<p>如：多线程遍历一个很大的二叉树查找一个数据时，某一个线程找到了数据，那其它线程就没有必要继续执行了，可以取消它们了。</p>
<p>注意<code>pthread_cancel()</code>并不等待线程终止，它仅仅提出请求。 而目标线程收到这个请求也不会立即终止，要执行到取消点才能被取消。</p>
<p>取消有2种状态：</p>
<ul>
<li>允许<ul>
<li>异步cancel</li>
<li>推迟cancel(默认) 推迟至cancel点再取消</li>
</ul>
</li>
<li>不允许。</li>
</ul>
<p>POSIX 定义的cancel点，都是可能引发阻塞的系统调用(比如open(),read(),write()..)</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">fd1 = open();</span><br><span class="line"><span class="comment">// if err</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//fd1刚打开，就收到一个pthread_cancel(但这里不是cancel点，所以不会响应)</span></span><br><span class="line"><span class="comment">//&#x27;可能引发阻塞的系统调用&#x27;才是POSIX定义的cancel点，要执行到下一个cancel点才会响应</span></span><br><span class="line"><span class="comment">//所以，在下一个cancel点之前可以挂钩子函数去关闭/释放资源</span></span><br><span class="line">pthread_cleanup_push(close(fd1));</span><br><span class="line"></span><br><span class="line">fd2 = open();</span><br><span class="line"><span class="comment">// if err</span></span><br><span class="line"></span><br><span class="line">pthread_cleanup_push(close(fd2));</span><br><span class="line">...</span><br></pre></td></tr></table></figure>
<h3 id="pthread-setcanceltype"><a href="#pthread-setcanceltype" class="headerlink" title="pthread_setcanceltype"></a>pthread_setcanceltype</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//设置是否允许被取消</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_setcancelstate</span><span class="params">(<span class="keyword">int</span> state, <span class="keyword">int</span> *oldstate)</span></span>;</span><br><span class="line"><span class="comment">//设置取消方式(异步cancel还是推迟cancel)</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_setcanceltype</span><span class="params">(<span class="keyword">int</span> type, <span class="keyword">int</span> *oldtype)</span></span>;</span><br></pre></td></tr></table></figure>
<h3 id="pthread-testcancel"><a href="#pthread-testcancel" class="headerlink" title="pthread_testcancel"></a>pthread_testcancel</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//什么都不做，充当一个cancel点</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">pthread_testcancel</span><span class="params">(<span class="keyword">void</span>)</span></span>;</span><br></pre></td></tr></table></figure>
<h3 id="pthread-detach"><a href="#pthread-detach" class="headerlink" title="pthread_detach"></a>pthread_detach</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//分离一个线程</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_detach</span><span class="params">(<span class="keyword">pthread_t</span> thread)</span></span>;</span><br></pre></td></tr></table></figure>
<p>习惯上谁创建线程谁<code>pthread_join</code>收尸。当分离了thread线程，那就不能为其收尸。</p>
<h3 id="例0：多线程筛质数"><a href="#例0：多线程筛质数" class="headerlink" title="例0：多线程筛质数"></a>例0：多线程筛质数</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Filename: primer0.c</span></span><br><span class="line"><span class="comment"> * No.86.线程-线程竞争实例</span></span><br><span class="line"><span class="comment"> * Description:</span></span><br><span class="line"><span class="comment"> * 多线程去计算某一范围内的质数</span></span><br><span class="line"><span class="comment"> * $ gcc -Wall primer0.c -o primer0 -lpthread</span></span><br><span class="line"><span class="comment"> * Last modified: humble 2020-09-01 21:04:58</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LEFT (30000000)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> RIGHT (30000200)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> THRNUM (RIGHT - LEFT +1)</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">thr_primer</span><span class="params">(<span class="keyword">void</span> *p)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> **argv)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i,err;</span><br><span class="line">    <span class="keyword">pthread_t</span> tid[THRNUM];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i = LEFT; i &lt;= RIGHT; i++)&#123;</span><br><span class="line">        err = pthread_create(tid + (i - LEFT), <span class="literal">NULL</span>, thr_primer, (<span class="keyword">void</span> *)i); <span class="comment">//如果把i的地址则会有竞争</span></span><br><span class="line">        <span class="keyword">if</span>(err)&#123;</span><br><span class="line">            <span class="comment">//发生错误就收尸已经创建成功的线程</span></span><br><span class="line">            <span class="comment">//while(--i &lt; 0)&#123; pthread_join(tid[i - LEFT], NULL); &#125; //未测</span></span><br><span class="line">            <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;pthread_create():%s\n&quot;</span>, strerror(err));</span><br><span class="line">            <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i = LEFT; i &lt;= RIGHT; i++)&#123; <span class="comment">//收尸</span></span><br><span class="line">        pthread_join(tid[i - LEFT], <span class="literal">NULL</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">thr_primer</span><span class="params">(<span class="keyword">void</span> *p)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i, j, mark;</span><br><span class="line">    i = (<span class="keyword">int</span>)p;</span><br><span class="line">    mark = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span>(j = <span class="number">2</span>; j &lt; i/<span class="number">2</span>; j++) &#123;</span><br><span class="line">        <span class="keyword">if</span>(i % j == <span class="number">0</span>)&#123;</span><br><span class="line">            mark = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(mark == <span class="number">1</span>)&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%d is a primer\n&quot;</span>, i);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    pthread_exit(<span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [21:08:38]</span></span><br><span class="line">$ gcc -Wall primer0.c -o primer0 -lpthread</span><br><span class="line">primer0.c: In <span class="keyword">function</span> ‘main’:</span><br><span class="line">primer0.c:25:66: warning: cast to pointer from <span class="built_in">integer</span> of different size [-Wint-to-pointer-cast]</span><br><span class="line">   25 |         err = pthread_create(tid + (i - LEFT), NULL, thr_primer, (void *)i); //如果把i的地址则会有竞争</span><br><span class="line">      |                                                                  ^</span><br><span class="line">primer0.c: In <span class="keyword">function</span> ‘thr_primer’:</span><br><span class="line">primer0.c:45:9: warning: cast from pointer to <span class="built_in">integer</span> of different size [-Wpointer-to-int-cast]</span><br><span class="line">   45 |     i = (int)p;</span><br><span class="line">      |         ^</span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [21:08:40]</span></span><br><span class="line">$ ./primer0</span><br><span class="line">30000059 is a primer</span><br><span class="line">30000071 is a primer</span><br><span class="line">30000083 is a primer</span><br><span class="line">30000023 is a primer</span><br><span class="line">30000137 is a primer</span><br><span class="line">30000001 is a primer</span><br><span class="line">30000041 is a primer</span><br><span class="line">30000199 is a primer</span><br><span class="line">30000193 is a primer</span><br><span class="line">30000037 is a primer</span><br><span class="line">30000149 is a primer</span><br><span class="line">30000049 is a primer</span><br><span class="line">30000109 is a primer</span><br><span class="line">30000169 is a primer</span><br><span class="line">30000163 is a primer</span><br><span class="line">30000167 is a primer</span><br><span class="line">30000133 is a primer</span><br><span class="line">30000079 is a primer</span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [21:09:12]</span></span><br><span class="line">$</span><br></pre></td></tr></table></figure>
<h3 id="例e：多线程筛质数"><a href="#例e：多线程筛质数" class="headerlink" title="例e：多线程筛质数"></a>例e：多线程筛质数</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Filename: primer0_e.c</span></span><br><span class="line"><span class="comment"> * No.87.线程-线程竞争实例2</span></span><br><span class="line"><span class="comment"> * Description:</span></span><br><span class="line"><span class="comment"> * 多线程去计算某一范围内的质数</span></span><br><span class="line"><span class="comment"> * $ gcc -Wall primer0_e.c -o primer0_e -lpthread</span></span><br><span class="line"><span class="comment"> * Last modified: humble 2020-09-01 21:14:22</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LEFT (30000000)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> RIGHT (30000200)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> THRNUM (RIGHT - LEFT +1)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>&#123;</span></span><br><span class="line">    <span class="keyword">int</span> n;</span><br><span class="line">&#125;<span class="keyword">thrarg_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">thr_primer</span><span class="params">(<span class="keyword">void</span> *p)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> **argv)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i,err;</span><br><span class="line">    <span class="keyword">pthread_t</span> tid[THRNUM];</span><br><span class="line">    <span class="keyword">thrarg_t</span> *p = <span class="literal">NULL</span>;</span><br><span class="line">    <span class="keyword">void</span> *ptr;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i = LEFT; i &lt;= RIGHT; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        p = <span class="built_in">malloc</span>(<span class="keyword">sizeof</span>(<span class="keyword">thrarg_t</span>));</span><br><span class="line">        <span class="keyword">if</span>(!p)&#123;</span><br><span class="line">            perror(<span class="string">&quot;malloc()&quot;</span>);</span><br><span class="line">            <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        p-&gt;n = i;</span><br><span class="line"></span><br><span class="line">        err = pthread_create(tid + (i - LEFT), <span class="literal">NULL</span>, thr_primer, p);</span><br><span class="line">        <span class="keyword">if</span>(err)&#123;</span><br><span class="line">            <span class="comment">//while(--i &lt; 0)&#123; pthread_join(tid[i - LEFT], NULL);free(.ptr.); &#125; //未测</span></span><br><span class="line">            <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;pthread_create():%s\n&quot;</span>, strerror(err));</span><br><span class="line">            <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i = LEFT; i &lt;= RIGHT; i++)&#123;</span><br><span class="line">        pthread_join(tid[i - LEFT], &amp;ptr);</span><br><span class="line">        <span class="built_in">free</span>(ptr);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">thr_primer</span><span class="params">(<span class="keyword">void</span> *p)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i, j, mark;</span><br><span class="line">    i = ((<span class="keyword">thrarg_t</span> *)p)-&gt;n;</span><br><span class="line">    <span class="comment">//free(p);</span></span><br><span class="line">    mark = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span>(j = <span class="number">2</span>; j &lt; i/<span class="number">2</span>; j++)&#123;</span><br><span class="line">        <span class="keyword">if</span>(i % j == <span class="number">0</span>)&#123;</span><br><span class="line">            mark = <span class="number">0</span>;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(mark == <span class="number">1</span>)&#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%d is a primer\n&quot;</span>, i);</span><br><span class="line">    &#125;</span><br><span class="line">    pthread_exit(p); <span class="comment">//把p返回给pthread_join</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>运行结果同上。例子创建了200个线程，但能进程空间允许创建的线程数量有限（可能PID耗尽，也可能栈空间耗尽）。每个线程消耗一个PID，并且消耗一份栈空间<code>ulimit -s</code>(32位进程栈最多创建约300个线程)，使用线程属性修改线程栈大小可提高最大线程创建量。使用<code>ulimit -a</code>命令可以查看所有资源上限。</p>
<h2 id="3-线程同步"><a href="#3-线程同步" class="headerlink" title="3.线程同步"></a>3.线程同步</h2><h3 id="互斥量-pthead-mutex-t"><a href="#互斥量-pthead-mutex-t" class="headerlink" title="互斥量(pthead_mutex_t)"></a>互斥量(pthead_mutex_t)</h3><p>可以使各个线程实现互斥的效果。由它来保护临界区每次只能由一个线程进入。当一个线程想要进入临界区之前需要先抢锁（加锁），如果能抢到锁就进入临界区工作，并且要在离开的时候解锁以便让其它线程可以抢到锁进入临界区；如果没有抢到锁则进入阻塞状态等待锁被释放然后再抢锁。</p>
<p>要在进入临界区之前加锁，在退出临界区的时候解锁。临界区是每个线程要单独执行的，所以临界区中的代码执行时间越短越好。互斥量限制的是一段代码能否执行，而不是一个变量或一个资源。</p>
<p>防止死锁：在临界区内跳转到临界区外时记得先解锁。</p>
<h3 id="pthread-mutex-xxx"><a href="#pthread-mutex-xxx" class="headerlink" title="pthread_mutex_xxx"></a>pthread_mutex_xxx</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//静态初始化，定义mutex时用宏初始化，使用默认属性</span></span><br><span class="line"><span class="keyword">pthread_mutex_t</span> mutex = PTHREAD_MUTEX_INITIALIZER;</span><br><span class="line"></span><br><span class="line"><span class="comment">//动态初始化，先定义未被初始化的mutex，再用 pthread_mutex_init 函数对mutex初始化，并指定属性mutexattr。返回0（永远不出错）</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_mutex_init</span><span class="params">(<span class="keyword">pthread_mutex_t</span> *mutex, <span class="keyword">const</span> <span class="keyword">pthread_mutexattr_t</span> *mutexattr)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//销毁mutex锁，成功返回0，失败返回errno</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_mutex_destroy</span><span class="params">(<span class="keyword">pthread_mutex_t</span> *mutex)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//阻塞死等抢锁，成功返回0，失败返回errno</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_mutex_lock</span><span class="params">(<span class="keyword">pthread_mutex_t</span> *mutex)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//非死等抢锁，成功返回0，失败返回errno</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_mutex_trylock</span><span class="params">(<span class="keyword">pthread_mutex_t</span> *mutex)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//解锁，成功返回0，失败返回errno</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_mutex_unlock</span><span class="params">(<span class="keyword">pthread_mutex_t</span> *mutex)</span></span>;</span><br></pre></td></tr></table></figure>
<h3 id="例：多线程读写文件"><a href="#例：多线程读写文件" class="headerlink" title="例：多线程读写文件"></a>例：多线程读写文件</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Filename: add.c</span></span><br><span class="line"><span class="comment"> * No.88.线程-竞争故障</span></span><br><span class="line"><span class="comment"> * Description: 线程冲突(线程互斥量)</span></span><br><span class="line"><span class="comment"> * $ gcc -Wall add.c -o add -lpthread</span></span><br><span class="line"><span class="comment"> * $ echo 1 &gt; /tmp/out</span></span><br><span class="line"><span class="comment"> * $ ./add &amp;&amp; cat /tmp/out</span></span><br><span class="line"><span class="comment"> * Last modified: humble 2020-09-02 09:03:58</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> THRNUM (20)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LINEBUFSIZE (1024)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> FNAME <span class="meta-string">&quot;/tmp/out&quot;</span></span></span><br><span class="line"></span><br><span class="line"><span class="comment">//demo0 不带互斥量，同时读写资源发生冲突</span></span><br><span class="line"><span class="comment">//demo1 使用互斥量，争抢资源，非同时读写</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> demo (1) <span class="comment">//0,1</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> (demo == 1)</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">pthread_mutex_t</span> mut = PTHREAD_MUTEX_INITIALIZER;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">thr_add</span><span class="params">(<span class="keyword">void</span> *p)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    FILE *fp;</span><br><span class="line">    <span class="keyword">char</span> linebuf[LINEBUFSIZE];</span><br><span class="line"></span><br><span class="line">    fp = fopen(FNAME, <span class="string">&quot;r+&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span>(!fp)&#123;</span><br><span class="line">        perror(<span class="string">&quot;fopen()&quot;</span>);</span><br><span class="line">        <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> (demo == 1)</span></span><br><span class="line">    pthread_mutex_lock(&amp;mut);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">    fgets(linebuf, LINEBUFSIZE, fp);</span><br><span class="line">    fseek(fp, <span class="number">0</span>, SEEK_SET);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> (demo == 0)</span></span><br><span class="line">    sleep(<span class="number">1</span>); <span class="comment">//扩大竞争时间域，等全部线程都读取完毕，再往下操作</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">    <span class="built_in">fprintf</span>(fp, <span class="string">&quot;%d\n&quot;</span>, atoi(linebuf) + <span class="number">1</span>);</span><br><span class="line">    fclose(fp); <span class="comment">//刷新缓冲区，不能放到unlock后面</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> (demo == 1)</span></span><br><span class="line">    pthread_mutex_unlock(&amp;mut);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">    pthread_exit(<span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> **argv)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i, err;</span><br><span class="line">    <span class="keyword">pthread_t</span> tid[THRNUM];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; THRNUM; i++)&#123;</span><br><span class="line">        err = pthread_create(tid + i, <span class="literal">NULL</span>, thr_add, <span class="literal">NULL</span>);</span><br><span class="line">        <span class="keyword">if</span>(err)&#123;</span><br><span class="line">            <span class="comment">//while(--i &lt; 0)&#123; pthread_join(tid[i - LEFT], NULL); &#125; //未测</span></span><br><span class="line">            <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;pthread_create():%s\n&quot;</span>, strerror(err));</span><br><span class="line">            <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; THRNUM; i++)&#123;</span><br><span class="line">        pthread_join(tid[i], <span class="literal">NULL</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">if</span> (demo == 1)</span></span><br><span class="line">    pthread_mutex_destroy(&amp;mut); <span class="comment">//对应 pthread_mutex_init()</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [9:46:35]</span></span><br><span class="line">$ gcc -Wall add.c -o add -lpthread <span class="comment">#demo0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [9:46:40]</span></span><br><span class="line">$ <span class="built_in">echo</span> 1 &gt; /tmp/out</span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [9:46:42]</span></span><br><span class="line">$ ./add</span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [9:46:46]</span></span><br><span class="line">$ cat /tmp/out</span><br><span class="line">2</span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [9:46:48]</span></span><br><span class="line">$ vi add.c</span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [9:47:07]</span></span><br><span class="line">$ gcc -Wall add.c -o add -lpthread <span class="comment">#demo1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [9:47:10]</span></span><br><span class="line">$ <span class="built_in">echo</span> 1 &gt; /tmp/out</span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [9:47:12]</span></span><br><span class="line">$ ./add</span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [9:47:14]</span></span><br><span class="line">$ cat /tmp/out</span><br><span class="line">21</span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [9:47:16]</span></span><br><span class="line">$</span><br></pre></td></tr></table></figure>
<h3 id="例：4个线程分别打印abcd，且按顺序，持续5秒"><a href="#例：4个线程分别打印abcd，且按顺序，持续5秒" class="headerlink" title="例：4个线程分别打印abcd，且按顺序，持续5秒"></a>例：4个线程分别打印abcd，且按顺序，持续5秒</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Filename: abcd.c</span></span><br><span class="line"><span class="comment"> * No.89.线程-互斥量</span></span><br><span class="line"><span class="comment"> * Description:4个线程分别打印abcd，且按顺序，持续5秒</span></span><br><span class="line"><span class="comment"> * $ gcc -Wall abcd.c -o abcd -lpthread</span></span><br><span class="line"><span class="comment"> * Last modified: humble 2020-09-02 10:06:45</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> THRNUM (4)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">pthread_mutex_t</span> mut[THRNUM];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">next</span><span class="params">(<span class="keyword">int</span> n)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(n + <span class="number">1</span> == THRNUM)&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> n + <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">thr_abcd</span><span class="params">(<span class="keyword">void</span> *p)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> n = (<span class="keyword">int</span>)p;</span><br><span class="line">    <span class="keyword">int</span> c = <span class="string">&#x27;a&#x27;</span> + (<span class="keyword">int</span>)p;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)&#123;</span><br><span class="line">        pthread_mutex_lock(mut + n); <span class="comment">//死等着抢属于自己的锁</span></span><br><span class="line">        write(<span class="number">1</span>, &amp;c, <span class="number">1</span>);</span><br><span class="line">        pthread_mutex_unlock(mut + next(n)); <span class="comment">//放开下一个人的锁</span></span><br><span class="line">    &#125;</span><br><span class="line">    pthread_exit(<span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> **argv)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i, err;</span><br><span class="line">    <span class="keyword">pthread_t</span> tid[THRNUM];</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; THRNUM; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        pthread_mutex_init(mut + i, <span class="literal">NULL</span>);</span><br><span class="line">        pthread_mutex_lock(mut + i); <span class="comment">//初始化完，立刻抢锁</span></span><br><span class="line">        err = pthread_create(tid + i, <span class="literal">NULL</span>, thr_abcd, (<span class="keyword">void</span> *)i);</span><br><span class="line">        <span class="keyword">if</span>(err)&#123;</span><br><span class="line">            <span class="comment">//while(--i &lt; 0)&#123; pthread_join(tid[i - LEFT], NULL); &#125; //未测</span></span><br><span class="line">            <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;pthread_create():%s\n&quot;</span>, strerror(err));</span><br><span class="line">            <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//线程创建完毕时全部锁都抢到了</span></span><br><span class="line"></span><br><span class="line">    pthread_mutex_unlock(mut + <span class="number">0</span>); <span class="comment">//放开a的锁</span></span><br><span class="line">    alarm(<span class="number">5</span>); <span class="comment">//5秒钟后进程被信号杀死</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt; THRNUM; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        pthread_join(tid[i], <span class="literal">NULL</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [11:20:10] C:142</span></span><br><span class="line">$ gcc -Wall abcd.c -o abcd -lpthread</span><br><span class="line">abcd.c: In <span class="keyword">function</span> ‘thr_abcd’:</span><br><span class="line">abcd.c:29:13: warning: cast from pointer to <span class="built_in">integer</span> of different size [-Wpointer-to-int-cast]</span><br><span class="line">   29 |     int n = (int)p;</span><br><span class="line">      |             ^</span><br><span class="line">abcd.c:30:19: warning: cast from pointer to <span class="built_in">integer</span> of different size [-Wpointer-to-int-cast]</span><br><span class="line">   30 |     int c = <span class="string">&#x27;a&#x27;</span> + (int)p;</span><br><span class="line">      |                   ^</span><br><span class="line">abcd.c: In <span class="keyword">function</span> ‘main’:</span><br><span class="line">abcd.c:49:55: warning: cast to pointer from <span class="built_in">integer</span> of different size [-Wint-to-pointer-cast]</span><br><span class="line">   49 |         err = pthread_create(tid + i, NULL, thr_abcd, (void *)i);</span><br><span class="line">      |                                                       ^</span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [11:21:54]</span></span><br><span class="line">$ ./abcd</span><br><span class="line">abcdabcdabcdabcdabcdabcdabcda...</span><br><span class="line">[1]    20197 alarm      ./abcd</span><br></pre></td></tr></table></figure>
<h3 id="让出cpu"><a href="#让出cpu" class="headerlink" title="让出cpu"></a>让出cpu</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//让出当前线程所占用的调度器给其它线程使用，而不必等待时间片耗尽才切换调度器</span></span><br><span class="line"><span class="comment">//成功返回0，失败返回-1并设置errno</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sched_yield</span><span class="params">(<span class="keyword">void</span>)</span></span>;</span><br></pre></td></tr></table></figure>
<p>可以把它理解成一个很短暂的 <code>sleep()</code> 。一般用于在使用一个资源时需要同时获得多把锁但是却没法一次性获得全部的锁的场景下，只要有任何一把锁没有抢到，那么就立即释放已抢到的锁，并让出自己的调度器让其它线程有机会获得被自己释放的锁。当再次调度到自己时再重新抢锁，直到能一次性抢到所有的锁时再进入临界区，这样就避免了出现死锁的情况。</p>
<h3 id="例：N个线程去计算某一范围内的质数"><a href="#例：N个线程去计算某一范围内的质数" class="headerlink" title="例：N个线程去计算某一范围内的质数"></a>例：N个线程去计算某一范围内的质数</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* Filename: primer0_pool.c</span></span><br><span class="line"><span class="comment"> * No.90.线程-线程池实现</span></span><br><span class="line"><span class="comment"> * Description: N个线程去计算某一范围内的质数</span></span><br><span class="line"><span class="comment"> * 查询法/忙等版，main和多个thr_primer一起抢锁，会出现main长时间抢不到锁的情况</span></span><br><span class="line"><span class="comment"> * $ gcc -Wall primer0_pool.c -o primer0_pool -lpthread</span></span><br><span class="line"><span class="comment"> * Last modified: humble 2020-09-02 15:18:52</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdlib.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;sched.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> LEFT (30000000)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> RIGHT (30000200)</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> THRNUM (4)</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">int</span> num = <span class="number">0</span>; <span class="comment">//猪圈</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">pthread_mutex_t</span> mut_num = PTHREAD_MUTEX_INITIALIZER; <span class="comment">//猪圈锁</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">thr_primer</span><span class="params">(<span class="keyword">void</span> *p)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">int</span> argc, <span class="keyword">char</span> **argv)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i,err;</span><br><span class="line">    <span class="keyword">pthread_t</span> tid[THRNUM];</span><br><span class="line"></span><br><span class="line">    <span class="comment">//创建线程(创建4头猪)</span></span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt;= THRNUM; i++)&#123;</span><br><span class="line">        err = pthread_create(tid + i, <span class="literal">NULL</span>, thr_primer, (<span class="keyword">void</span> *)i);</span><br><span class="line">        <span class="keyword">if</span>(err)&#123;</span><br><span class="line">            <span class="comment">//while(--i &lt; 0)&#123; pthread_join(tid[i], NULL); &#125; //未测</span></span><br><span class="line">            <span class="built_in">fprintf</span>(<span class="built_in">stderr</span>, <span class="string">&quot;pthread_create():%s\n&quot;</span>, strerror(err));</span><br><span class="line">            <span class="built_in">exit</span>(<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//main把数字投放到num里面(主人把猪食扔到猪圈里)</span></span><br><span class="line">    <span class="keyword">for</span>(i = LEFT; i &lt;= RIGHT; i++)&#123;</span><br><span class="line">        pthread_mutex_lock(&amp;mut_num); <span class="comment">//抢猪圈的锁</span></span><br><span class="line">        <span class="keyword">while</span>(num != <span class="number">0</span>)&#123;</span><br><span class="line">            <span class="comment">//上一份猪食居然还没被猪吃掉</span></span><br><span class="line">            <span class="comment">//解锁猪圈，好让猪能抢锁进猪圈吃掉</span></span><br><span class="line">            pthread_mutex_unlock(&amp;mut_num);</span><br><span class="line">            <span class="comment">//因为有可能某个thr_primer(猪)抢到也可能又被main(主人)抢到</span></span><br><span class="line">            <span class="comment">//所以操作调度器，让出cpu(可理解为段时间sleep)，避免接下来又是main(主人)抢到锁</span></span><br><span class="line">            sched_yield();</span><br><span class="line">            pthread_mutex_lock(&amp;mut_num);</span><br><span class="line">        &#125;</span><br><span class="line">        num = i; <span class="comment">//上一份猪食被猪吃掉了，投放猪食</span></span><br><span class="line">        pthread_mutex_unlock(&amp;mut_num); <span class="comment">//解锁猪圈，好让猪能抢锁进猪圈吃掉</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//到此，猪食已经投完了</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">//等猪取走猪食后，往猪圈投放一份猪粪(-1)，让猪别再抢锁进猪圈</span></span><br><span class="line">    pthread_mutex_lock(&amp;mut_num);</span><br><span class="line">    <span class="keyword">while</span>(num != <span class="number">0</span>)&#123;</span><br><span class="line">        <span class="comment">//上一份猪食居然还没被猪吃掉</span></span><br><span class="line">        <span class="comment">//解锁猪圈，好让猪能抢锁进猪圈吃掉</span></span><br><span class="line">        pthread_mutex_unlock(&amp;mut_num);</span><br><span class="line">        <span class="comment">//因为有可能别的thr_primer(猪)抢到也可能又被自己抢到</span></span><br><span class="line">        <span class="comment">//所以操作调度器，让出cpu(可理解为段时间sleep)，避免接下来又是main(主人)抢到锁</span></span><br><span class="line">        sched_yield();</span><br><span class="line">        pthread_mutex_lock(&amp;mut_num);</span><br><span class="line">    &#125;</span><br><span class="line">    num = <span class="number">-1</span>; <span class="comment">//猪食被猪吃光了，投放猪粪(-1)</span></span><br><span class="line">    pthread_mutex_unlock(&amp;mut_num); <span class="comment">//解锁猪圈，好让猪能抢锁进猪圈来发现猪粪(-1)而结束</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span>(i = <span class="number">0</span>; i &lt;= THRNUM; i++)</span><br><span class="line">    &#123;</span><br><span class="line">        pthread_join(tid[i], <span class="literal">NULL</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    pthread_mutex_destroy(&amp;mut_num);</span><br><span class="line">    <span class="built_in">exit</span>(<span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">thr_primer</span><span class="params">(<span class="keyword">void</span> *p)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> i, j, mark;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)&#123;</span><br><span class="line">        pthread_mutex_lock(&amp;mut_num); <span class="comment">//猪抢到锁</span></span><br><span class="line">        <span class="keyword">while</span>(num == <span class="number">0</span>)&#123;</span><br><span class="line">            <span class="comment">//发现猪圈是空的</span></span><br><span class="line">            <span class="comment">//解锁给main(主人)去投放猪食(有可能被其他thr_primer(猪)抢到)</span></span><br><span class="line">            pthread_mutex_unlock(&amp;mut_num);</span><br><span class="line">            sched_yield();  <span class="comment">//让出cpu，避免接下来又是自己抢到锁</span></span><br><span class="line">            pthread_mutex_lock(&amp;mut_num);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//猪圈终于有东西了</span></span><br><span class="line">        <span class="keyword">if</span>(num == <span class="number">-1</span>)&#123;</span><br><span class="line">            <span class="comment">//居然是猪粪，释放锁</span></span><br><span class="line">            pthread_mutex_unlock(&amp;mut_num);</span><br><span class="line">            <span class="keyword">break</span>; <span class="comment">//准备自杀</span></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//是猪食，取走，释放锁给主人或其他猪去抢</span></span><br><span class="line">        i = num;</span><br><span class="line">        num = <span class="number">0</span>;</span><br><span class="line">        pthread_mutex_unlock(&amp;mut_num);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//开吃</span></span><br><span class="line">        mark = <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span>(j = <span class="number">2</span>; j &lt; i/<span class="number">2</span>; j++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(i % j == <span class="number">0</span>)&#123;</span><br><span class="line">                mark = <span class="number">0</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span>(mark == <span class="number">1</span>)&#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;[%d] %d is a primer\n&quot;</span>, (<span class="keyword">int</span>)p, i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    pthread_exit(<span class="literal">NULL</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [15:10:33]</span></span><br><span class="line">$ gcc -Wall primer0_pool.c -o primer0_pool -lpthread</span><br><span class="line">primer0_pool.c: In <span class="keyword">function</span> ‘main’:</span><br><span class="line">primer0_pool.c:30:57: warning: cast to pointer from <span class="built_in">integer</span> of different size [-Wint-to-pointer-cast]</span><br><span class="line">   30 |         err = pthread_create(tid + i, NULL, thr_primer, (void *)i);</span><br><span class="line">      |                                                         ^</span><br><span class="line">primer0_pool.c: In <span class="keyword">function</span> ‘thr_primer’:</span><br><span class="line">primer0_pool.c:115:45: warning: cast from pointer to <span class="built_in">integer</span> of different size [-Wpointer-to-int-cast]</span><br><span class="line">  115 |             <span class="built_in">printf</span>(<span class="string">&quot;[%d] %d is a primer\n&quot;</span>, (int)p, i);</span><br><span class="line">      |                                             ^</span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [15:17:54]</span></span><br><span class="line">$ ./primer0_pool</span><br><span class="line">[0] 30000001 is a primer</span><br><span class="line">[1] 30000023 is a primer</span><br><span class="line">[3] 30000041 is a primer</span><br><span class="line">[2] 30000037 is a primer</span><br><span class="line">[4] 30000049 is a primer</span><br><span class="line">[0] 30000059 is a primer</span><br><span class="line">[4] 30000083 is a primer</span><br><span class="line">[3] 30000071 is a primer</span><br><span class="line">[2] 30000109 is a primer</span><br><span class="line">[1] 30000079 is a primer</span><br><span class="line">[4] 30000137 is a primer</span><br><span class="line">[3] 30000149 is a primer</span><br><span class="line">[0] 30000133 is a primer</span><br><span class="line">[4] 30000169 is a primer</span><br><span class="line">[3] 30000193 is a primer</span><br><span class="line">[2] 30000163 is a primer</span><br><span class="line">[1] 30000167 is a primer</span><br><span class="line">[0] 30000199 is a primer</span><br><span class="line"></span><br><span class="line"><span class="comment"># li @ evpower in ~/humble/tmp/lhq/parallel/thread/posix on git:master x [15:17:56]</span></span><br><span class="line">$</span><br></pre></td></tr></table></figure>
<h3 id="pthread-once"><a href="#pthread-once" class="headerlink" title="pthread_once"></a>pthread_once</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//定义一个 控制执行一次 的变量</span></span><br><span class="line"><span class="keyword">pthread_once_t</span> once_control = PTHREAD_ONCE_INIT;</span><br><span class="line"></span><br><span class="line"><span class="comment">//使用 once_control 的变量判断对init_routine()进行调用</span></span><br><span class="line"><span class="comment">//动态单次初始化，它能保证 init_routine 函数仅被调用一次</span></span><br><span class="line"><span class="comment">//返回0永远不出错</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_once</span><span class="params">(<span class="keyword">pthread_once_t</span> *once_control, <span class="keyword">void</span> (*init_routine) (<span class="keyword">void</span>))</span></span>;</span><br></pre></td></tr></table></figure>
<h3 id="例：查询法令牌桶"><a href="#例：查询法令牌桶" class="headerlink" title="例：查询法令牌桶"></a>例：查询法令牌桶</h3><p>把令牌桶写成库的方式提供给它人调用，需要解决并发调用的资源竞争问题。另外实现只在第一次使用时才加载模块<code>module_load()</code>，类似C++的构造函数。</p>
<p>在临界区需要调用一个函数，但是从程序的布局来看该函数内无法通过加锁来避免并发冲突/函数重入。根据 POSIX 标准的约定，这种函数的命名规则是必须以 _unlocked 作为后缀，所以大家在看到这样的函数时在调用之前一定要先加锁。例如下面代码中的 <code>get_free_pos_unlocked()</code>。</p>
<p><a target="_blank" rel="noopener" href="https://gitee.com/humble-zh/studyapue/tree/master/parallel/thread/posix/mytbf_mt">查看代码</a></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">./parallel/thread/posix/mytbf_mt/</span><br><span class="line"><span class="comment">#define demo (mutex_only)</span></span><br></pre></td></tr></table></figure>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix/mytbf_mt on git:master x [19:17:09] C:2</span></span><br><span class="line">$ tree</span><br><span class="line">.</span><br><span class="line">├── main.c</span><br><span class="line">├── Makefile</span><br><span class="line">├── mytbf.c</span><br><span class="line">└── mytbf.h</span><br><span class="line"></span><br><span class="line">0 directories, 4 files</span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix/mytbf_mt on git:master x [19:17:10]</span></span><br><span class="line">$ make</span><br><span class="line">cc -Wall   -c -o main.o main.c</span><br><span class="line">cc -Wall   -c -o mytbf.o mytbf.c</span><br><span class="line">gcc -Wall main.o mytbf.o -o mytbf -lpthread</span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix/mytbf_mt on git:master x [19:17:16]</span></span><br><span class="line">$ ./mytbf /etc/services</span><br><span class="line"><span class="comment"># Network services, Internet style</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># Note that i^C</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># mi @ evpower in ~/humble/tmp/lhq/parallel/thread/posix/mytbf_mt on git:master x [19:17:29] C:130</span></span><br><span class="line">$</span><br></pre></td></tr></table></figure>
<p>上面的程序经过测试，发现 CPU 正在满负荷工作，说明程序中出现了忙等。就是 <code>mytbf_fetchtoken()</code> 函数获得锁的时候采用了忙等的方式。异步程序有两种处理方式，一种是通知法，一种是查询法。我们这里用的就是查询法，下面改成通知法实现。</p>
<h3 id="条件变量-pthead-cond-t"><a href="#条件变量-pthead-cond-t" class="headerlink" title="条件变量(pthead_cond_t)"></a>条件变量(pthead_cond_t)</h3><p>让线程以无竞争的形式等待某个条件的发生，当条件发生时通知等待的线程醒来去做某件事。另一种方式是把所有等待同一个条件的线程都唤醒</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//静态初始化，定义cond时用宏初始化，使用默认属性</span></span><br><span class="line"><span class="keyword">pthread_cond_t</span> cond = PTHREAD_COND_INITIALIZER;</span><br><span class="line"></span><br><span class="line"><span class="comment">//动态初始化，先定义未被初始化的cond，再用 pthread_cond_init 函数对cond初始化，并指定属性cond_attr</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_cond_init</span><span class="params">(<span class="keyword">pthread_cond_t</span> *cond, <span class="keyword">pthread_condattr_t</span> *cond_attr)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//函数用于唤醒当前多个等待的线程中的任何一个</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_cond_signal</span><span class="params">(<span class="keyword">pthread_cond_t</span> *cond)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//惊群，将现在正在等待的线程全部唤醒</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_cond_broadcast</span><span class="params">(<span class="keyword">pthread_cond_t</span> *cond)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//在临界区外阻塞等待某一个条件发生变化，(死等)直到有一个通知到来打断它的等待，被唤醒后开始抢mutex锁</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_cond_wait</span><span class="params">(<span class="keyword">pthread_cond_t</span> *cond, <span class="keyword">pthread_mutex_t</span> *mutex)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//增加了超时功能的等待，(尝试等)超时之后无论能否拿到锁都返回</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_cond_timedwait</span><span class="params">(<span class="keyword">pthread_cond_t</span> *cond, <span class="keyword">pthread_mutex_t</span> *mutex, <span class="keyword">const</span> struct  timespec</span></span></span><br><span class="line"><span class="function"><span class="params">*abstime)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//销毁cond锁</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">pthread_cond_destroy</span><span class="params">(<span class="keyword">pthread_cond_t</span> *cond)</span></span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">//全部函数成功返回0，失败返回errno</span></span><br></pre></td></tr></table></figure>
<p>把查询法（忙等）修改为通知法（非忙等）仅仅加一个条件变量（pthread_cond_t） 就行了。</p>
<h3 id="例：通知法实现令牌桶"><a href="#例：通知法实现令牌桶" class="headerlink" title="例：通知法实现令牌桶"></a>例：通知法实现令牌桶</h3><p><a target="_blank" rel="noopener" href="https://gitee.com/humble-zh/studyapue/tree/master/parallel/thread/posix/mytbf_mt">查看代码</a></p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">./parallel/thread/posix/mytbf_mt/</span><br><span class="line"><span class="comment">#define demo (with_cond)</span></span><br></pre></td></tr></table></figure>
<p>通常<code>cond_wait()</code>等待会放在一个循环中，就像上面的令牌桶栗子一样，因为可能有多个线程都在等待条件满足，当前的线程’等待条件’被唤醒时不代表’执行条件’一定满足。可能先被唤醒的线程发现条件满足已经去工作了，等轮到当前线程调度的时候条件可能就又不满足了，所以如果条件不满足又通过<code>while</code>继续进入等待。</p>
<p>用 pthread_cond_signal(3) 还是用 pthread_cond_broadcast(3) 呢？</p>
<p>根据具体场景来选择。一般只有一个线程在等待或者明确知道哪个线程应该被唤醒的时候使用 _signal() 函数，如果有多个线程在等待并且不确定应该由谁起来工作的时候使用惊群。不确定是指业务上不能确定哪个线程应该工作，而不是你作为程序猿稀里糊涂的不知道哪个线程该工作。程序猿应该保证了解你的每一行代码在做什么，而不要写出一坨自己都不知道它在做什么的代码。</p>
<h2 id="4-线程属性-amp-同步属性"><a href="#4-线程属性-amp-同步属性" class="headerlink" title="4.线程属性&amp;同步属性"></a>4.线程属性&amp;同步属性</h2><h2 id="5-重入-amp-线程与信号-amp-线程与fork"><a href="#5-重入-amp-线程与信号-amp-线程与fork" class="headerlink" title="5.重入&amp;线程与信号&amp;线程与fork"></a>5.重入&amp;线程与信号&amp;线程与fork</h2>
        
        <br />
        <div id="comment-container">
        </div>
        <div id="disqus_thread"></div>

        <div id="lv-container">
        </div>

    </div>
</div>

    </div>
</div>


<footer class="footer">
    <ul class="list-inline text-center">
        
        

        

        

        

        

    </ul>
    
    <p>
        <span id="busuanzi_container_site_pv">
            <span id="busuanzi_value_site_pv"></span>PV
        </span>
        <span id="busuanzi_container_site_uv">
            <span id="busuanzi_value_site_uv"></span>UV
        </span>
        Created By <a target="_blank" rel="noopener" href="https://hexo.io/">Hexo</a>  Theme <a target="_blank" rel="noopener" href="https://github.com/aircloud/hexo-theme-aircloud">AirCloud</a></p>
</footer>




</body>

<script>
    // We expose some of the variables needed by the front end
    window.hexo_search_path = "search.json"
    window.hexo_root = "/"
    window.isPost = true
</script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>

<script src="/js/index.js"></script>

<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>




</html>
